/***************************************************************************
 *   Copyright (C) 2008 by Chris Bornholdt,,,   *
 *   chris.bornholdt@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "main.hpp"
#include <vector>
#include <stack>
#include <iostream>
#include <algorithm>
#include <numeric>
#include <boost/lambda/lambda.hpp>
#include "objectarea.hpp"
#include "convolution.hpp"
#include "convolutionkernels.hpp"
#include "convolutionresampling.hpp"

using namespace boost::lambda;

QTEST_MAIN(ObjectAreaTest);

void ObjectAreaTest::testBasics()
{
	typedef object_area<int, std::vector> int_area;
	
	int_area test_area1(100, 100), test_area2, test_area3;
	
	for(int j = 0; j < 100; j++)
		for(int i = 0; i < 100; i++)
			test_area1(i, j) = i + 100 * j;
			
	int sum = 0;
	for(int j = 0; j < 100; j++)
		for(int i = 0; i < 100; i++)
			sum += test_area1(i, j);
			
	QVERIFY(sum == 10000 * 9999 / 2);
			
	test_area2 = *test_area1.sub_area(10, 10, 10, 10);
	
	sum = 0;
	for(int j = 0; j < 10; j++)
		for(int i = 0; i < 10; i++)
			sum += test_area2(i, j);
			
	QVERIFY(sum == 10 * 9 / 2 * 10 * 100 + 1000 * 10 * 10 + 10 * 10 * 10 + 10 * 10 * 9 / 2);
	
	test_area3.resize(10, 10);
	for(int j = 0; j < 10; j++)
		for(int i = 0; i < 10; i++)
			test_area3(i, j) = i + j;
			
	sum = 0;
	for(int j = 0; j < 10; j++)
		for(int i = 0; i < 10; i++)
			sum += test_area3(i, j);
			
	QVERIFY(sum == 10 * 10 * 9);
}

void ObjectAreaTest::testAreaOperations()
{
	typedef object_area<int, std::vector> int_area;
	
	int_area test_area1(100, 100), test_area2, test_area3(10, 10);
	
	for(int j = 0; j < 10; j++)
		for(int i = 0; i < 10; i++)
			test_area3(i, j) = 1;
			
	test_area1.blit_area(test_area3);
	test_area1.blit_area(test_area3, 90, 0);
	test_area1.blit_area(test_area3, 0, 90);
	test_area1.blit_area(test_area3, 90, 90);
	
	int sum = 0;
	for(int j = 0; j < 100; j++)
		for(int i = 0; i < 100; i++)
			sum += test_area1(i, j);
			
	QVERIFY(sum == 4 * 10 * 10);
	
	sum = std::accumulate(test_area1.begin(), test_area1.end(), 0);
	QVERIFY(sum == 400);
	
	const int_area& area = test_area3;
	
	int_area::strip_list strip_list = *test_area3.strips();
	int_area::const_strip_list cstrip_list = *area.strips();
	
	int_area::vstrip_list vstrip_list = *test_area3.vstrips();
	int_area::const_vstrip_list cvstrip_list = *area.vstrips();
	
	int_area::strip_list::iterator iter = strip_list.begin();
	int_area::vstrip_list::iterator viter = vstrip_list.begin();
	
	int n = 0;
	for(; iter != strip_list.end(); ++iter, ++n)
		std::fill(iter->first, iter->second, n);
		
	sum = std::accumulate(test_area3.begin(), test_area3.end(), 0);
	QVERIFY(sum == 450);
	
	n = 0;
	for(; viter != vstrip_list.end(); ++viter, ++n)
		std::fill(viter->first, viter->second, n);
		 
	sum = std::accumulate(test_area3.begin(), test_area3.end(), 0);
	QVERIFY(sum == 450);
	
//	int idx = 0, max_idx = std::min(strip_list.size(), vstrip_list.size());
//	for(sum = 0, iter = strip_list.begin(), viter = vstrip_list.begin(); idx < max_idx;
//		++idx, ++iter, ++viter);
		
}	

void ObjectAreaTest::discreteConvolutionTest()
{
	std::vector<double> test_vector1(10), test_vector2(100), smoothing_kernel1(3), smoothing_kernel2(5), 
		edge_kernel1(2), edge_kernel2(2), result1, result2, result3;
	
	std::fill(test_vector1.begin(), test_vector1.end(), 1.0);
	smoothing_kernel1[0] = smoothing_kernel1[2] = .25;
	smoothing_kernel1[1] = .5;
	edge_kernel1[0] = edge_kernel2[1] = -1;
	edge_kernel1[1] = edge_kernel2[0] = 1;
	
	/*	fill test_vector2 with 10 bands of 5 consecutive 1's	*/
	for(int i = 0; i < 10; i++)
		std::fill_n(test_vector2.begin() + 10 * i, 5, 1.0);
		
	result1 = discretely_convolve<false>(test_vector2, edge_kernel1);
	
	/*	accumlate sum of the squares in result		*/
	double sum = std::accumulate(result1.begin(), result1.end(), 0.0, _1 + _2 * _2);
	QVERIFY(sum == 20);	//number of changes in test_vector2
	
	result2 = discretely_convolve<false>(test_vector2, edge_kernel2);
	sum = std::accumulate(result2.begin(), result2.end(), 0.0, _1 + _2 * _2);
	QVERIFY(sum == 20);	//should be same as result1, just reversed results
	
	result3.resize(result2.size());
	std::transform(result1.begin(), result1.end(), result2.begin(), result3.begin(), std::plus<double>());
	sum = std::accumulate(result3.begin(), result3.end(), 0.0);
	QVERIFY(sum == 0);
}

void ObjectAreaTest::generalConvolutionTest()
{
	std::vector<double> test_vector1(10), result1;
	
	std::fill(test_vector1.begin(), test_vector1.end(), 1);
	result1 = convolve<false>(test_vector1, rectangular_pulse<double>(-1, 1));
}

void ObjectAreaTest::bicubicResamplingTest()
{
	std::vector<double> test_vector1, result1;
	
	for(int n = 10; n < 1000; ++n )
	{
		double lastdiff = 1e25;;
		double i;
		double sum1, sum2;
		i = 0;
		test_vector1.resize(n);
		std::generate(test_vector1.begin(), test_vector1.end(), var(i)++ * var(i));
		sum1 = std::accumulate(test_vector1.begin() + 1, test_vector1.end() - 1, 0);
		sum1 += (test_vector1.front() + test_vector1.back()) / 2;
		sum1 /= (test_vector1.size() - 1);
		
		result1 = bicubic_resampling(test_vector1, n * 2.5);
		sum2 = std::accumulate(result1.begin() + 1, result1.end() - 1, 0);
		sum2 += (result1.front() + result1.back()) / 2;
		sum2 /= (result1.size() - 1);
		
		QVERIFY(std::fabs(sum2 - sum1) / sum1 < 0.1);
		QVERIFY(std::fabs(sum2 - sum1) < lastdiff);
		lastdiff = std::fabs(sum2 - sum1);
	}
}
